﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClusterAggregation.DataSets
{
    /**
    * Class Representation of a partition (literature also lists it as a Clustering).
    */
    public class CPartition
    {
        /** 
        * _clusters is a list of data that is contained in this partition in the form of a list. 
        * note that it's public to save in performance.
        */
        public List<CCluster> _clusters;

        /**
         * Name of the partition.
         */
        public string name = "";
        /**
         * ANMI criterion for ensembles
         */
        public double ANMI = 0;

        /** 
        * more intuitive access to _clusters via getter.
        */
        public List<CCluster> clusters
        {
            get
            {
                return _clusters;
            }
        } // no setter

        /**
         * basic constructor
         */
        public CPartition()
        {
            _clusters = new List<CCluster>();
        }

        /**
         * Comparator to a List of partitions(!) by ANMI criterion.
         * @param b (CPartition[])
         * @return ANMI value of comparison
         */
        public double compareTo(CPartition[] b)
        {
            double sum = 0;
            for (int i = 0; i < b.Length; i++)
            {
                sum += this.compareTo(b[i]);
            }
            return sum / b.Length;
        }
        /**
         * Comparator to a another partition by NMI criterion.
         */
        public double compareTo(CPartition b)
        {
            double sum = 0;
            double norm1 = 0, norm2 = 0;
            double tmp, cnt;
            double Nhl; // number of shared objects
            //double unions; // size of union
            double n = 0;
            LinkedList<CCluster> ths = new LinkedList<CCluster>();
            LinkedList<CCluster> hm = new LinkedList<CCluster>();

            foreach(CCluster cls in this._clusters)
            {
                n += cls._data.Count();
                if (cls._data.Count() != 0)
                    ths.AddFirst(cls);
            }
            foreach (CCluster cls in b._clusters)
            {
                if (cls._data.Count() != 0)
                    hm.AddFirst(cls);
            }

            //  entropy
            foreach (CCluster cls in ths)
            {
                tmp = cls._data.Count();
                norm1 += tmp * Math.Log(tmp / n, 2);
            }
            if (norm1 == 0) norm1 = n / (n + 1);  // if 0, append to 1 - happens when entire data is in 1 cluster

            foreach (CCluster cls in hm)
            {
                tmp = cls._data.Count();
                norm2 += tmp * Math.Log(tmp / n, 2);
            }
            if (norm2 == 0) norm2 = n / (n + 1);  // if 0, append to 1 - happens when entire data is in 1 cluster

            // 1 cluster has all the objects
            if (norm1 == 0)
                norm1 = -1;
            if (norm2 == 0)
                norm2 = -1;

            foreach (CCluster cls1 in ths)
            {
                foreach (CCluster cls2 in hm)
                {
                    Nhl = cls1._data.Intersect(cls2._data).Count();
                   // unions = cls1._data.Union(cls2._data).Count();

                    if (Nhl != 0)
                    {
                        cnt = cls1.data.Count() * cls2.data.Count(); // calculate buttom
                        if (cnt == 0)
                            return 0.0;
                        tmp = Nhl * Math.Log(Nhl * n / cnt, 2); // calculate upper
                        if (double.IsNaN(tmp))
                            sum += n / (n + 1);
                        else 
                            sum += tmp;
                    }
                }
            }

            return sum / (Math.Sqrt(norm1*norm2) );
        }
    }
}
